import pendulum
from datetime import datetime
from sqlalchemy import or_
from flask_sqlalchemy import SessionBase
from werkzeug.security import generate_password_hash

from . import models

class Users:
    @classmethod
    def save(self, db: SessionBase, user: models.User):
        db.add(user)
        db.commit()
        db.refresh(user)
        return user

    @classmethod
    def create(self, db: SessionBase, email: str, password: str, role: models.Role, name: str, avatar: str):
        hashed_password = generate_password_hash(password)
        user = models.User(
            email=email, role=role, name=name, avatar=avatar, hashed_password=hashed_password)
        db.add(user)
        db.commit()
        db.refresh(user)
        return user

    @classmethod
    def get_users(self, db: SessionBase):
        return db.query(models.User).all()

    @classmethod
    def get_user(self, db: SessionBase, id: int):
        return db.query(models.User).filter(models.User.id == id).first()

    @classmethod
    def get_user_by_email(self, db: SessionBase, email: str):
        return db.query(models.User).filter(models.User.email == email).first()

    @classmethod
    def get_all_students(self, db: SessionBase):
        return db.query(models.User).filter(models.User.role == models.Role.student).all()

    @classmethod
    def get_all_professors(self, db: SessionBase):
        return db.query(models.User).filter(models.User.role == models.Role.professor).all()

class Courses:
    @classmethod
    def save(self, db: SessionBase, course: models.Course):
        db.add(course)
        db.commit()
        db.refresh(course)
        return course

    @classmethod
    def get_course(self, db: SessionBase, id: int):
        return db.query(models.Course).filter(models.Course.id == id).first()

    @classmethod
    def get_courses(self, db: SessionBase, user_id: int):
        ids = set()
        participants = db.query(models.Participant).filter(models.Participant.user_id == user_id).all()
        for participant in participants:
            ids.add(participant.course_id)
        filter = or_(models.Course.id.in_(ids), models.Course.professor_id == user_id)
        return db.query(models.Course).filter(filter).all()

    @classmethod
    def get_all_courses(self, db: SessionBase):
        return db.query(models.Course).all()

class Timetables:
    @classmethod
    def get_timetable(self, db: SessionBase, id: int):
        return db.query(models.Timetable).filter(models.Timetable.id == id).first()

    @classmethod
    def get_timetables_by_course_id(self, db: SessionBase, course_id:int):
        return db.query(models.Timetable).filter(models.Timetable.course_id == course_id).all()

    @classmethod
    def get_timetables(self, db: SessionBase, user_id: int):
        ids = set()
        courses = Courses.get_courses(db, user_id)
        for course in courses:
            ids.add(course.id)
        q = db.query(models.Timetable)
        q = q.filter(models.Timetable.course_id.in_(ids), models.Timetable.state != models.CourseState.finished)
        return q.order_by(models.Timetable.start_time).all()

    @classmethod
    def get_timetables_of_week(self, db: SessionBase, user_id: int):
        today = pendulum.now()
        end = today.end_of('week')
        start = today.start_of('week')

        ids = set()
        courses = Courses.get_courses(db, user_id)
        for course in courses:
            ids.add(course.id)
        q = db.query(models.Timetable)
        q = q.filter(
            models.Timetable.course_id.in_(ids),
            models.Timetable.state != models.CourseState.finished,
            models.Timetable.start_time >= start,
            models.Timetable.end_time < end
        )
        return q.order_by(models.Timetable.start_time).all()

    @classmethod
    def open_lesson(self, db: SessionBase, id: int):
        db.query(models.Timetable).filter(models.Timetable.id == id).update({
            'state': models.CourseState.signing_in, 'open_time': datetime.now(),
        })
        db.commit()
        return Timetables.get_timetable(db, id)

    @classmethod
    def start_lesson(self, db: SessionBase, id: int):
        db.query(models.Timetable).filter(models.Timetable.id == id).update({'state': models.CourseState.started})
        db.commit()
        return Timetables.get_timetable(db, id)

    @classmethod
    def end_lesson(self, db: SessionBase, id: int):
        db.query(models.Timetable).filter(models.Timetable.id == id).update({'state': models.CourseState.finished})
        db.commit()
        return Timetables.get_timetable(db, id)

class Attendances:
    @classmethod
    def has(self, db: SessionBase, timetable_id: int, user_id: int):
        record = db.query(models.Attendance).filter(models.Attendance.timetable_id == timetable_id, models.Attendance.user_id == user_id).first()
        return not record is None

    @classmethod
    def add(self, db: SessionBase, timetable_id: int, user_id: int):
        record = models.Attendance(timetable_id=timetable_id, user_id=user_id)
        db.add(record)
        db.commit()
        db.refresh(record)

        db.query(models.Timetable).filter(models.Timetable.id == timetable_id).update(
            {'number_of_sign_in': models.Timetable.number_of_sign_in + 1}
        )
        return record

    @classmethod
    def get_has_by_user_id(self, db: SessionBase, user_id: int):
        return db.query(models.Attendance).filter(models.Attendance.user_id == user_id).all()

